package com.sinodata.bsm.cicp.collector.sqlserver;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;

import com.sinodata.bsm.cicp.collector.AbstractJDBCCollector;
import com.sinodata.bsm.cicp.exception.CollectorException;
import com.sinodata.bsm.cicp.util.DBConnectionUtil;
import com.sinodata.bsm.common.constants.DataTypeConstants;
import com.sinodata.bsm.common.vo.ResPropertyValue;

public class ErrorLog extends AbstractJDBCCollector {
    private static final Logger logger = Logger.getLogger(ErrorLog.class);
    private static String TAGNAME = "checkPoint";
    private static String SEPARATOR = ",";
    private static String SQLSERVER2000 = "2000";
    private static String SQLSERVER2005 = "2005";

    @Override
    public List<ResPropertyValue> collect(String ip) throws CollectorException {
        String serverName = this.getParamMap().get("ServerName");
        String errorFlags = this.getParamMap().get("ErrorID");
        String tmpFileUrl = getTmpFileUrl(ip, errorFlags);
        String checkPoint = getCheckPoint(tmpFileUrl);
        CallableStatement statement = null;
        ResultSet rst = null;
        CallableStatement cstmtLog = null;
        ResultSet rstLog = null;
        Connection conn = null;
        try {
            conn = DBConnectionUtil.getInstance().getConnection("sqlserver", ip, serverName);
            //sqlserver version
            String sql = "sp_getsvrinfo";
            statement = conn.prepareCall(sql);
            statement.setQueryTimeout(10);
            rst = statement.executeQuery();
            String version = SQLSERVER2000;
            while (rst.next()) {
                String name = rst.getString(2);
                if (name.equals("FileVersion")) {
                    version = rst.getString(4);
                    break;
                }
            }
            //collect error log
            sql = "master.dbo.xp_readerrorlog";
            cstmtLog = conn.prepareCall(sql);
            rstLog = cstmtLog.executeQuery();
            List result = null;
            if (version.contains(SQLSERVER2000)) {
                result = getErrorLog2000(rstLog, errorFlags, checkPoint, tmpFileUrl);
            } else if (version.contains(SQLSERVER2005)) {
                result = getErrorLog2005(rstLog, errorFlags, checkPoint, tmpFileUrl);
            }
            return result;
        } catch (SQLException ex) {
            logger.error("when query SQL Server database " + serverName, ex);
            throw new CollectorException(ex);

        } catch (IOException ioe) {
            logger.error("when query SQL Server " + serverName, ioe);
            throw new CollectorException(ioe);

        } finally {
            closeResultSet(rst);
            closeStatement(statement);
            closeResultSet(rstLog);
            closeStatement(cstmtLog);
            DBConnectionUtil.getInstance().returnConnection(conn);
        }
    }

    @Override
    public String[] getParamMeta() {
        return new String[] { "ServerName", "ErrorID" };
    }

    @Override
    public String[] getPropMeta() {
        return new String[] { "IsCreateNewErrors", "LogContents" };
    }

    private List<ResPropertyValue> getErrorLog2000(ResultSet rstLog, String errorFlag, String checkPoint, String tmpFileUrl) throws CollectorException {
        List<ResPropertyValue> matchedLogs = new ArrayList<ResPropertyValue>();
        try {
            while (rstLog.next()) {
                String errorLog = rstLog.getString(1);
                String logtime = errorLog.substring(0, 22);
                if (checkPoint != null && logtime.compareTo(checkPoint) < 0) {
                    continue;
                }
                checkPoint = logtime;
                String message = errorLog.substring(33, errorLog.length());
                if (isMatched(message, errorFlag)) {
                    ResPropertyValue propertyValueX = new ResPropertyValue();
                    ResPropertyValue.PropValue[] propValues = new ResPropertyValue.PropValue[2];
                    // propValues[0] = ResPropertyValue.createPropValue("是", getPropMeta()[0]);
                    propValues[0] = ResPropertyValue.createPropValue(DataTypeConstants.BOOLEAN_VALUE_TRUE, getPropMeta()[0]);
                    propValues[1] = ResPropertyValue.createPropValue(message, getPropMeta()[1]);
                    propertyValueX.setValues(propValues);
                    matchedLogs.add(propertyValueX);
                }
            }
            if (matchedLogs.size() == 0) {
                ResPropertyValue propertyValueX = new ResPropertyValue();
                ResPropertyValue.PropValue[] propValues = new ResPropertyValue.PropValue[2];
                //propValues[0] = ResPropertyValue.createPropValue("否", getPropMeta()[0]);
                propValues[0] = ResPropertyValue.createPropValue(DataTypeConstants.BOOLEAN_VALUE_FALSE, getPropMeta()[0]);
                propValues[1] = ResPropertyValue.createPropValue(" ", getPropMeta()[1]);
                propertyValueX.setValues(propValues);
                matchedLogs.add(propertyValueX);
            }
        } catch (SQLException e) {
            logger.error("failed to get error log", e);
            throw new CollectorException(e);
        }
        setCheckPoint(checkPoint, tmpFileUrl);
        return matchedLogs;
    }

    private List<ResPropertyValue> getErrorLog2005(ResultSet rstLog, String errorFlag, String checkPoint, String tmpFileUrl) throws CollectorException {
        List<ResPropertyValue> matchedLogs = new ArrayList<ResPropertyValue>();
        try {
            String logtime = "";
            while (rstLog.next()) {
                logtime = rstLog.getString(1);
                if (checkPoint != null && logtime.compareTo(checkPoint) <= 0) {
                    continue;
                }
                String message = rstLog.getString(3);
                if (isMatched(message, errorFlag)) {
                    ResPropertyValue propertyValueX = new ResPropertyValue();
                    ResPropertyValue.PropValue[] propValues = new ResPropertyValue.PropValue[2];
                    // propValues[0] = ResPropertyValue.createPropValue("是", getPropMeta()[0]);
                    propValues[0] = ResPropertyValue.createPropValue(DataTypeConstants.BOOLEAN_VALUE_TRUE, getPropMeta()[0]);
                    propValues[1] = ResPropertyValue.createPropValue(message, getPropMeta()[1]);
                    propertyValueX.setValues(propValues);
                    matchedLogs.add(propertyValueX);
                }
            }
            checkPoint = logtime;
            if (matchedLogs.size() == 0) {
                ResPropertyValue propertyValueX = new ResPropertyValue();
                ResPropertyValue.PropValue[] propValues = new ResPropertyValue.PropValue[2];
                //propValues[0] = ResPropertyValue.createPropValue("否", getPropMeta()[0]);
                propValues[0] = ResPropertyValue.createPropValue(DataTypeConstants.BOOLEAN_VALUE_FALSE, getPropMeta()[0]);
                propValues[1] = ResPropertyValue.createPropValue(" ", getPropMeta()[1]);
                propertyValueX.setValues(propValues);
                matchedLogs.add(propertyValueX);
            }
        } catch (SQLException e) {
            logger.error("failed to get error log", e);
            throw new CollectorException(e);
        }
        setCheckPoint(checkPoint, tmpFileUrl);
        return matchedLogs;
    }

    private boolean isMatched(String line, String errorFlag) {
        String[] flags = errorFlag.split(SEPARATOR);
        for (int i = 0; i < flags.length; i++) {
            if (line.contains(flags[i])) {
                return true;
            }
        }
        return false;
    }

    private static void closeResultSet(ResultSet rst) throws CollectorException {
        try {
            if (rst != null)
                rst.close();
        } catch (SQLException ex) {
            logger.error("failed to close resultset" + ex);
            throw new CollectorException(ex);
        }
    }

    private static void closeStatement(Statement sta) throws CollectorException {
        try {
            if (sta != null)
                sta.close();
        } catch (SQLException ex) {
            logger.error("failed to close statement", ex);
            throw new CollectorException(ex);
        }
    }

    /**
     * 临时文件名
     * @param ip
     * @param filePath
     * @return
     */
    private String getTmpFileUrl(String ip, String errorFlag) {
        return "tmp/IP" + ip + errorFlag + "SQLServerErrLog.xml";
    }

    private String getCheckPoint(String tmpFileUrl) throws CollectorException {
        String checkPoint = null;
        try {
            Document doc = getTmpXml(tmpFileUrl);
            Element root = doc.getRootElement();
            Element element = root.element(TAGNAME);
            if (element != null) {
                checkPoint = element.getText();
            }
        } catch (IOException e) {

            logger.error("failed to get check point", e);
            throw new CollectorException(e);
        }
        return checkPoint;
    }

    private void setCheckPoint(String checkPoint, String tmpFileUrl) throws CollectorException {
        try {
            Document doc = getTmpXml(tmpFileUrl);
            Element root = doc.getRootElement();
            Element element = root.element(TAGNAME);
            if (element == null) {
                element = root.addElement("checkPoint");
            }
            element.setText(checkPoint);
            saveXml(doc, tmpFileUrl);
        } catch (IOException e) {
            logger.error("failed to set check point", e);
            throw new CollectorException(e);
        }
    }

    //////////////////////////////////////////////////////
    private Document getTmpXml(String tmpFileUrl) throws IOException {
        File tmpXml = new File(tmpFileUrl);
        if (!tmpXml.exists()) {
            createNewXMLFile(tmpFileUrl);
        }
        SAXReader saxReader = new SAXReader();
        Document doc = null;
        try {
            doc = saxReader.read(tmpXml);
        } catch (DocumentException e) {
            logger.error("failed to get temp XML", e);

        }
        return doc;
    }

    private void createNewXMLFile(String tmpFileUrl) throws IOException {
        Document doc = DocumentHelper.createDocument();
        doc.addElement("root");
        saveXml(doc, tmpFileUrl);
    }

    private void saveXml(Document doc, String tmpFileUrl) throws IOException {
        OutputFormat format = new OutputFormat("", true, "UTF-8");
        XMLWriter writer = null;
        try {
            writer = new XMLWriter(new FileWriter(new File(tmpFileUrl)), format);
            writer.write(doc);
        } catch (Exception e) {
            logger.error("failed to save XML", e);
            throw new IOException(e.getMessage());
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }

}
